package cn.yywd_123.akarin.server.utils;

/*
 *  Author: yywd_123
 *  Date: 2022/11/20
 */

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SocketServer implements AutoCloseable {
	private final ServerSocket serverSocket;
	private final UserManager userManager = new UserManager();
	private final int threadCount;
	private boolean isServerRunning = false;

	public SocketServer(int port, int thread) throws IOException {
		serverSocket = new ServerSocket(port);
		threadCount = thread;
	}

	public void start() {
		if (!isServerRunning) {
			isServerRunning = true;
			System.out.println("Akarin-Server已启动 作者:yywd_123");
			showServerInfo();
			System.out.println("输入help可查看帮助信息");
			new Thread(() -> {
				ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
				while (isServerRunning) {
					try {
						Socket s = serverSocket.accept();
						if (userManager.isIpBanned(s.getInetAddress().getCanonicalHostName())) {
							s.getOutputStream().write("您的ip已被禁止进入此服务器!".getBytes(StandardCharsets.UTF_8));
							s.getOutputStream().flush();
							continue;
						}
						if (userManager.getUserCount() >= threadCount) {
							s.getOutputStream().write("服务器已满!".getBytes(StandardCharsets.UTF_8));
							s.getOutputStream().flush();
							continue;
						}
						executorService.execute(new ClientHandler(s));
					} catch (Exception ignore) {
					}
				}
			}, "客户端响应线程").start();

			new Thread(() -> {
				while (isServerRunning) {
					userManager.forEach(ClientHandler::checkClientStatus);
					try {
						TimeUnit.SECONDS.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}, "客户端状态检查线程").start();
		}
	}

	@Override
	public void close() throws Exception {
		serverSocket.close();
		isServerRunning = false;
	}

	private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

	public void sendMessageToAllClient(String msg, Socket socket) {
		userManager.forEach((clientHandler -> clientHandler.send((socket == null ? "Server > " : "") + msg)));
		if (socket == null) System.out.printf("Server > %s\n", msg);
		else
			System.out.printf("ip:[%s], time:[%s] | %s\n", socket.getInetAddress().getCanonicalHostName(), dateFormat.format(Calendar.getInstance().getTime()), msg);
	}

	public void showServerInfo() {
		System.out.printf("-----------------------------\n" +
						"服务器信息:\n" +
						"端口: %d\n" +
						"最高连接人数: %d\n" +
						"在线人数: %d\n-----------------------------\n",
				serverSocket.getLocalPort(),
				threadCount,
				userManager.getUserCount());
	}

	public UserManager getUserManager() {
		return userManager;
	}

	class ClientHandler extends Thread implements AutoCloseable {
		private final Socket s;
		private boolean isClientHandlerRunning;

		private boolean isLogon = false;
		private String userName;

		public ClientHandler(Socket socket) {
			s = socket;
			isClientHandlerRunning = true;
		}

		@Override
		public void run() {
			while (isClientHandlerRunning && !s.isClosed()) {
				try {
					if (!isLogon) {
						send("请输入你的用户名!");
						TimeUnit.SECONDS.sleep(1);
						if ((userName = readContent()) != null && !userName.isEmpty()) {
							if (userName.contains(" ")) {
								send("用户名不得包含空格!");
								continue;
							}
							if (userManager.isUserBanned(userName)) {
								send("此用户名已被禁止加入此服务器!");
								continue;
							}
							userManager.addUser(userName, this);
							sendMessageToAllClient(userName + "加入了服务器", null);
							isLogon = true;
						}
					} else {
						String string = readContent();
						if (!string.isEmpty()) {
							if (userManager.isUserMuted(userName)) send("您已被服务器禁言!");
							else sendMessageToAllClient(userName + " > " + string, s);
						}
					}
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		}

		public String readContent() {
			try {
				s.setSoTimeout(10000);
				StringBuilder sb = new StringBuilder();
				byte[] buf = new byte[8192];
				int len = 0;
				try {
					while ((len = s.getInputStream().read(buf)) > 0) {
						if (len < buf.length) buf[len] = '\n';
						sb.append(new String(buf, 0, len));
						if (len < buf.length) break;
					}
				} catch (Exception e) {
					if (len < buf.length) buf[len] = '\n';
					sb.append(new String(buf, 0, len));
				}
				return sb.toString();
			} catch (Exception e) {
				checkClientStatus();
				return null;
			}
		}

		public void send(String data) {
			try {
				OutputStream os = s.getOutputStream();
				os.write(data.getBytes(StandardCharsets.UTF_8));
				os.flush();
			} catch (IOException e) {
				checkClientStatus();
			}
		}

		public void checkClientStatus() {
			if (s.isClosed()) close();
			else {
				int i = 0;
				while (true) {
					try {
						TimeUnit.MICROSECONDS.sleep(100);
						s.sendUrgentData(' ');
						return;
					} catch (Exception e) {
						if (e instanceof IOException) ++i;
						else return;
						if (i >= 3) {
							close();
							return;
						}
					}
				}
			}
		}

		@Override
		public void close() {
			try {
				isClientHandlerRunning = false;
				userManager.removeUser(userName);
				s.close();
				sendMessageToAllClient(userName + "退出了服务器", null);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

		public String getClientIp() {
			return s.getInetAddress().getCanonicalHostName();
		}
	}
}
